Subject: fix for vulnerability CVE-2010-0402 for OpenTTD 0.7.0 - 0.7.5 (Denial of service via improperly validated commands)
From: OpenTTD developer team <info@openttd.org>
Origin: backport, https://github.com/OpenTTD/OpenTTD/commit/1f28e23 https://github.com/OpenTTD/OpenTTD/commit/a929ab0 https://github.com/OpenTTD/OpenTTD/commit/0f65601 https://github.com/OpenTTD/OpenTTD/commit/75d4bc9 https://github.com/OpenTTD/OpenTTD/commit/2a5ddd0 https://github.com/OpenTTD/OpenTTD/commit/5ecf2f7 https://github.com/OpenTTD/OpenTTD/commit/2365a4d
Bug: https://github.com/OpenTTD/OpenTTD/issues/3748 https://github.com/OpenTTD/OpenTTD/issues/3747

In multiple places in-game commands are not properly validated that allow
remote attackers to cause a denial of service (crash) and possibly execute
arbitrary code via unspecified vectors.

The bug is exploitable only in-game so the attacker must have access to
the server: his IP must not be banned, he must know the password if it
has been set and the server must not be full.

The one root cause of this problem is a generalised “pool” system that uses
size_t to store/access indices, but has typed validation checks to check
indices. Some of these checks use 16 bit integers, which means that a 32 bit
“command parameter” gets silently (by the compiler) truncated to 16 bits before
being checked after which the 32 bit number is used to access items in the
pool.

For example item 0 exists, the pool has that one item and the command parameter
is 65536. The validation function would be checking 0, as the high 16 bits of
the parameter would be (silently) discarded, and as such the check returns that
it is a valid item. This then results in trying to get item 65536 which is way
outside of the valid range of the pool.

However, it is not solely confined to pooled items; there are a few cases where
a not-yet-validated value is used.

Attached are patches for all vulnerable versions since 0.6.0. Note that this is
in essence a partial backport of trunk r19657, r19656, r19655, r19654, r19637,
r19633 and r19621. The “in essence” is because those patches change the whole
handling of command parameters which is 90% changing to conform to the new
policy or standard and 10% actually fixes the crashes. Therefor we chose to
make one minimalistic patch for each of the stable series since 0.6 that solves
all issues we are aware of, without ending up with a ten times larger diff with
many unrelated unneeded changes. Also earlier releases were more vulnerable
than the later ones due to improvements in the pool system and some rewrites.

Due to the time needed for assembling these patches and because we are unaware
of any Linux distribution shipping OpenTTD from before the 0.6 series in a
still supported release we have not made patches for those. They are vulnerable
though.

This fix maintains network compatability of patched and unpatched OpenTTD’s
except the cases where the unpatched version would crash, but then the crash
would be the cause of the disconnection and not that they are incompatible
on that part of the network communication.


Index: src/terraform_cmd.cpp
===================================================================
--- src/terraform_cmd.cpp	(revision 19678)
+++ src/terraform_cmd.cpp	(working copy)
@@ -364,7 +364,7 @@
 	uint oldh = TileHeight(p1);

 	/* compute new height */
-	uint h = oldh + p2;
+	uint h = oldh + (int8)p2;

 	/* Check range of destination height */
 	if (h > MAX_TILE_HEIGHT) return_cmd_error((oldh == 0) ? STR_1003_ALREADY_AT_SEA_LEVEL : STR_1004_TOO_HIGH);
Index: src/rail_cmd.cpp
===================================================================
--- src/rail_cmd.cpp	(revision 19678)
+++ src/rail_cmd.cpp	(working copy)
@@ -306,8 +306,8 @@
 CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
 	Slope tileh;
-	RailType railtype = (RailType)p1;
-	Track track = (Track)p2;
+	RailType railtype = (RailType)GB(p1, 0, 4);
+	Track track = (Track)GB(p2, 0, 3);
 	TrackBits trackbit;
 	CommandCost cost(EXPENSES_CONSTRUCTION);
 	CommandCost ret;
@@ -442,12 +442,12 @@
  */
 CommandCost CmdRemoveSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
-	Track track = (Track)p2;
+	Track track = (Track)GB(p2, 0, 3);
 	TrackBits trackbit;
 	CommandCost cost(EXPENSES_CONSTRUCTION, _price.remove_rail );
 	bool crossing = false;

-	if (!ValParamTrackOrientation((Track)p2)) return CMD_ERROR;
+	if (!ValParamTrackOrientation(track)) return CMD_ERROR;
 	trackbit = TrackToTrackBits(track);

 	/* Need to read tile owner now because it may change when the rail is removed
@@ -751,6 +751,7 @@
 	Slope tileh;

 	/* check railtype and valid direction for depot (0 through 3), 4 in total */
+	p1 = GB(p1, 0, 4);
 	if (!ValParamRailtype((RailType)p1)) return CMD_ERROR;

 	tileh = GetTileSlope(tile, NULL);
@@ -824,6 +825,7 @@
 	uint num_dir_cycle = GB(p1, 15, 2);

 	if (sigtype > SIGTYPE_LAST) return CMD_ERROR;
+	if (cycle_start > cycle_stop || cycle_stop > SIGTYPE_LAST) return CMD_ERROR;

 	/* You can only build signals on plain rail tiles, and the selected track must exist */
 	if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) ||
@@ -1040,10 +1042,9 @@
 	bool semaphores = HasBit(p2, 4);
 	bool remove = HasBit(p2, 5);
 	bool autofill = HasBit(p2, 6);
-	Trackdir trackdir = TrackToTrackdir(track);
 	byte signal_density = GB(p2, 24, 8);

-	if (p1 >= MapSize()) return CMD_ERROR;
+	if (p1 >= MapSize() || !ValParamTrackOrientation(track)) return CMD_ERROR;
 	end_tile = p1;
 	if (signal_density == 0 || signal_density > 20) return CMD_ERROR;

@@ -1053,6 +1054,7 @@
 	 * since the original amount will be too dense (shorter tracks) */
 	signal_density *= 2;

+	Trackdir trackdir = TrackToTrackdir(track);
 	if (CmdFailed(ValidateAutoDrag(&trackdir, tile, end_tile))) return CMD_ERROR;

 	track = TrackdirToTrack(trackdir); // trackdir might have changed, keep track in sync
@@ -1250,7 +1252,7 @@
 CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
 	CommandCost cost(EXPENSES_CONSTRUCTION);
-	RailType totype = (RailType)p2;
+	RailType totype = (RailType)GB(p2, 0, 4);

 	if (!ValParamRailtype(totype)) return CMD_ERROR;
 	if (p1 >= MapSize()) return CMD_ERROR;
Index: src/group_cmd.cpp
===================================================================
--- src/group_cmd.cpp	(revision 19678)
+++ src/group_cmd.cpp	(working copy)
@@ -77,7 +77,7 @@
  */
 CommandCost CmdCreateGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
-	VehicleType vt = (VehicleType)p1;
+	VehicleType vt = (VehicleType)GB(p1, 0, 3);
 	if (!IsCompanyBuildableVehicleType(vt)) return CMD_ERROR;

 	if (!Group::CanAllocateItem()) return CMD_ERROR;
@@ -105,6 +105,7 @@
  */
 CommandCost CmdDeleteGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
+	p1 = GB(p1, 0, 16);
 	if (!IsValidGroupID(p1)) return CMD_ERROR;

 	Group *g = GetGroup(p1);
@@ -164,6 +165,7 @@
  */
 CommandCost CmdRenameGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
+	p1 = GB(p1, 0, 16);
 	if (!IsValidGroupID(p1)) return CMD_ERROR;

 	Group *g = GetGroup(p1);
@@ -199,6 +201,8 @@
  */
 CommandCost CmdAddVehicleGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
+	p1 = GB(p1, 0, 16);
+	p2 = GB(p2, 0, 16);
 	GroupID new_g = p1;

 	if (!IsValidVehicleID(p2) || (!IsValidGroupID(new_g) && !IsDefaultGroupID(new_g))) return CMD_ERROR;
@@ -246,12 +250,12 @@
  */
 CommandCost CmdAddSharedVehicleGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
-	VehicleType type = (VehicleType)p2;
+	p1 = GB(p1, 0, 16);
+	VehicleType type = (VehicleType)GB(p2, 0, 3);
 	if (!IsValidGroupID(p1) || !IsCompanyBuildableVehicleType(type)) return CMD_ERROR;

 	if (flags & DC_EXEC) {
 		Vehicle *v;
-		VehicleType type = (VehicleType)p2;
 		GroupID id_g = p1;

 		/* Find the first front engine which belong to the group id_g
@@ -283,7 +287,8 @@
  */
 CommandCost CmdRemoveAllVehiclesGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
-	VehicleType type = (VehicleType)p2;
+	p1 = GB(p1, 0, 16);
+	VehicleType type = (VehicleType)GB(p2, 0, 3);
 	if (!IsValidGroupID(p1) || !IsCompanyBuildableVehicleType(type)) return CMD_ERROR;

 	Group *g = GetGroup(p1);
@@ -320,6 +325,7 @@
  */
 CommandCost CmdSetGroupReplaceProtection(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
+	p1 = GB(p1, 0, 16);
 	if (!IsValidGroupID(p1)) return CMD_ERROR;

 	Group *g = GetGroup(p1);
Index: src/vehiclelist.h
===================================================================
--- src/vehiclelist.h	(revision 19678)
+++ src/vehiclelist.h	(working copy)
@@ -9,7 +9,7 @@

 typedef SmallVector<const Vehicle *, 32> VehicleList;

-void GenerateVehicleSortList(VehicleList *list, VehicleType type, Owner owner, uint32 index, uint16 window_type);
+bool GenerateVehicleSortList(VehicleList *list, VehicleType type, Owner owner, uint32 index, uint16 window_type);
 void BuildDepotVehicleList(VehicleType type, TileIndex tile, VehicleList *engine_list, VehicleList *wagon_list, bool individual_wagons = false);

 #endif /* VEHICLELIST_H */
Index: src/station_cmd.cpp
===================================================================
--- src/station_cmd.cpp	(revision 19678)
+++ src/station_cmd.cpp	(working copy)
@@ -1356,6 +1356,7 @@
  */
 CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
+	p1 = GB(p1, 0, 2);
 	bool type = HasBit(p2, 0);
 	bool is_drive_through = HasBit(p2, 1);
 	bool build_over_road  = is_drive_through && IsNormalRoadTile(tile);
@@ -2911,6 +2912,7 @@
  */
 CommandCost CmdRenameStation(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
+	p1 = GB(p1, 0, 16);
 	if (!IsValidStationID(p1)) return CMD_ERROR;

 	Station *st = GetStation(p1);
Index: src/vehiclelist.cpp
===================================================================
--- src/vehiclelist.cpp	(revision 19678)
+++ src/vehiclelist.cpp	(working copy)
@@ -67,8 +67,9 @@
  *      <li>VLW_WAYPOINT_LIST: index of waypoint to generate a list for</li>
  *    </ul>
  * @param window_type The type of window the list is for, using the VLW_ flags in vehicle_gui.h
+ * @return false if invalid list is requested
  */
-void GenerateVehicleSortList(VehicleList *list, VehicleType type, Owner owner, uint32 index, uint16 window_type)
+bool GenerateVehicleSortList(VehicleList *list, VehicleType type, Owner owner, uint32 index, uint16 window_type)
 {
 	list->Clear();

@@ -92,7 +93,10 @@

 		case VLW_SHARED_ORDERS:
 			/* Add all vehicles from this vehicle's shared order list */
-			for (v = GetVehicle(index); v != NULL; v = v->NextShared()) {
+			v = IsValidVehicleID(index) ? GetVehicle(index) : NULL;
+			if (v == NULL || v->type != type || !v->IsPrimaryVehicle()) return false;
+
+			for (; v != NULL; v = v->NextShared()) {
 				*list->Append() = v;
 			}
 			break;
@@ -144,8 +148,9 @@
 			}
 			break;

-		default: NOT_REACHED(); break;
+		default: return false;
 	}

 	list->Compact();
+	return true;
 }
Index: src/ship_cmd.cpp
===================================================================
--- src/ship_cmd.cpp	(revision 19678)
+++ src/ship_cmd.cpp	(working copy)
@@ -723,6 +723,7 @@
  */
 CommandCost CmdBuildShip(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
+	p1 = GB(p1, 0, 16);
 	UnitID unit_num;

 	if (!IsEngineBuildable(p1, VEH_SHIP, _current_company)) return_cmd_error(STR_SHIP_NOT_AVAILABLE);
Index: src/order_cmd.cpp
===================================================================
--- src/order_cmd.cpp	(revision 19678)
+++ src/order_cmd.cpp	(working copy)
@@ -442,7 +442,7 @@

 	v = GetVehicle(veh);

-	if (!CheckOwnership(v->owner)) return CMD_ERROR;
+	if (!v->IsPrimaryVehicle() || !CheckOwnership(v->owner)) return CMD_ERROR;

 	/* Check if the inserted order is to the correct destination (owner, type),
 	 * and has the correct flags if any */
@@ -512,8 +512,6 @@
 						default: return CMD_ERROR;
 					}
 				}
-			} else {
-				if (!IsCompanyBuildableVehicleType(v)) return CMD_ERROR;
 			}

 			if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE && v->type != VEH_TRAIN && v->type != VEH_ROAD) return CMD_ERROR;
@@ -669,15 +667,15 @@
 CommandCost CmdDeleteOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
 	Vehicle *v;
-	VehicleID veh_id = p1;
-	VehicleOrderID sel_ord = p2;
+	VehicleID veh_id = GB(p1, 0, 16);
+	VehicleOrderID sel_ord = GB(p2, 0, 8);
 	Order *order;

 	if (!IsValidVehicleID(veh_id)) return CMD_ERROR;

 	v = GetVehicle(veh_id);

-	if (!CheckOwnership(v->owner)) return CMD_ERROR;
+	if (!v->IsPrimaryVehicle() || !CheckOwnership(v->owner)) return CMD_ERROR;

 	/* If we did not select an order, we maybe want to de-clone the orders */
 	if (sel_ord >= v->GetNumOrders())
@@ -737,14 +735,14 @@
 CommandCost CmdSkipToOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
 	Vehicle *v;
-	VehicleID veh_id = p1;
-	VehicleOrderID sel_ord = p2;
+	VehicleID veh_id = GB(p1, 0, 16);
+	VehicleOrderID sel_ord = GB(p2, 0, 8);

 	if (!IsValidVehicleID(veh_id)) return CMD_ERROR;

 	v = GetVehicle(veh_id);

-	if (!CheckOwnership(v->owner) || sel_ord == v->cur_order_index ||
+	if (!v->IsPrimaryVehicle() || !CheckOwnership(v->owner) || sel_ord == v->cur_order_index ||
 			sel_ord >= v->GetNumOrders() || v->GetNumOrders() < 2) return CMD_ERROR;

 	if (flags & DC_EXEC) {
@@ -776,14 +774,14 @@
  */
 CommandCost CmdMoveOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
-	VehicleID veh = p1;
+	VehicleID veh = GB(p1, 0, 16);
 	VehicleOrderID moving_order = GB(p2,  0, 16);
 	VehicleOrderID target_order = GB(p2, 16, 16);

 	if (!IsValidVehicleID(veh)) return CMD_ERROR;

 	Vehicle *v = GetVehicle(veh);
-	if (!CheckOwnership(v->owner)) return CMD_ERROR;
+	if (!v->IsPrimaryVehicle() || !CheckOwnership(v->owner)) return CMD_ERROR;

 	/* Don't make senseless movements */
 	if (moving_order >= v->GetNumOrders() || target_order >= v->GetNumOrders() ||
@@ -863,7 +861,7 @@
 	if (!IsValidVehicleID(veh)) return CMD_ERROR;

 	Vehicle *v = GetVehicle(veh);
-	if (!CheckOwnership(v->owner)) return CMD_ERROR;
+	if (!v->IsPrimaryVehicle() || !CheckOwnership(v->owner)) return CMD_ERROR;

 	/* Is it a valid order? */
 	if (sel_ord >= v->GetNumOrders()) return CMD_ERROR;
@@ -1079,7 +1077,7 @@

 	dst = GetVehicle(veh_dst);

-	if (!CheckOwnership(dst->owner)) return CMD_ERROR;
+	if (!dst->IsPrimaryVehicle() || !CheckOwnership(dst->owner)) return CMD_ERROR;

 	switch (p2) {
 		case CO_SHARE: {
@@ -1090,7 +1088,7 @@
 			src = GetVehicle(veh_src);

 			/* Sanity checks */
-			if (!CheckOwnership(src->owner) || dst->type != src->type || dst == src)
+			if (!src->IsPrimaryVehicle() || !CheckOwnership(src->owner) || dst->type != src->type || dst == src)
 				return CMD_ERROR;

 			/* Trucks can't share orders with busses (and visa versa) */
@@ -1135,7 +1133,7 @@
 			src = GetVehicle(veh_src);

 			/* Sanity checks */
-			if (!CheckOwnership(src->owner) || dst->type != src->type || dst == src)
+			if (!src->IsPrimaryVehicle() || !CheckOwnership(src->owner) || dst->type != src->type || dst == src)
 				return CMD_ERROR;

 			/* Trucks can't copy all the orders from busses (and visa versa),
@@ -1206,11 +1204,12 @@
 	CargoID cargo = GB(p2, 0, 8);
 	byte subtype  = GB(p2, 8, 8);

+	if (cargo >= NUM_CARGO) return CMD_ERROR;
 	if (!IsValidVehicleID(veh)) return CMD_ERROR;

 	v = GetVehicle(veh);

-	if (!CheckOwnership(v->owner)) return CMD_ERROR;
+	if (!v->IsPrimaryVehicle() || !CheckOwnership(v->owner)) return CMD_ERROR;

 	order = GetVehicleOrder(v, order_number);
 	if (order == NULL) return CMD_ERROR;
@@ -1359,12 +1358,13 @@
 	VehicleOrderID cur_ord = GB(p2,  0, 16);
 	uint16 serv_int = GB(p2, 16, 16);

+	p1 = GB(p1, 0, 16);
 	if (!IsValidVehicleID(p1)) return CMD_ERROR;

 	v = GetVehicle(p1);

 	/* Check the vehicle type and ownership, and if the service interval and order are in range */
-	if (!CheckOwnership(v->owner)) return CMD_ERROR;
+	if (!v->IsPrimaryVehicle() || !CheckOwnership(v->owner)) return CMD_ERROR;
 	if (serv_int != GetServiceIntervalClamped(serv_int) || cur_ord >= v->GetNumOrders()) return CMD_ERROR;

 	if (flags & DC_EXEC) {
Index: src/vehicle_cmd.cpp
===================================================================
--- src/vehicle_cmd.cpp	(revision 19678)
+++ src/vehicle_cmd.cpp	(working copy)
@@ -13,6 +13,7 @@
 #include "newgrf_engine.h"
 #include "newgrf_text.h"
 #include "functions.h"
+#include "cmd_helper.h"
 #include "window_func.h"
 #include "vehicle_func.h"
 #include "string_func.h"
@@ -134,15 +135,17 @@
 {
 	VehicleList list;
 	CommandCost return_value = CMD_ERROR;
-	VehicleType vehicle_type = (VehicleType)GB(p2, 0, 5);
+	VehicleType vehicle_type = (VehicleType)GB(p2, 0, 3);
 	bool start_stop = HasBit(p2, 5);
 	bool vehicle_list_window = HasBit(p2, 6);

+	if (!IsCompanyBuildableVehicleType(vehicle_type)) return CMD_ERROR;
+
 	if (vehicle_list_window) {
 		uint32 id = p1;
 		uint16 window_type = p2 & VLW_MASK;

-		GenerateVehicleSortList(&list, vehicle_type, _current_company, id, window_type);
+		if (!GenerateVehicleSortList(&list, vehicle_type, _current_company, id, window_type)) return CMD_ERROR;
 	} else {
 		/* Get the list of vehicles in the depot */
 		BuildDepotVehicleList(vehicle_type, tile, &list, NULL);
@@ -186,8 +189,10 @@

 	CommandCost cost(EXPENSES_NEW_VEHICLES);
 	uint sell_command;
-	VehicleType vehicle_type = (VehicleType)GB(p1, 0, 8);
+	VehicleType vehicle_type = (VehicleType)GB(p1, 0, 3);

+	if (!IsCompanyBuildableVehicleType(vehicle_type)) return CMD_ERROR;
+
 	switch (vehicle_type) {
 		case VEH_TRAIN:    sell_command = CMD_SELL_RAIL_WAGON; break;
 		case VEH_ROAD:     sell_command = CMD_SELL_ROAD_VEH;   break;
@@ -221,9 +226,10 @@
 {
 	VehicleList list;
 	CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES);
-	VehicleType vehicle_type = (VehicleType)GB(p1, 0, 8);
+	VehicleType vehicle_type = (VehicleType)GB(p1, 0, 3);
 	bool all_or_nothing = HasBit(p2, 0);

+	if (!IsCompanyBuildableVehicleType(vehicle_type)) return CMD_ERROR;
 	if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;

 	/* Get the list of vehicles in the depot */
@@ -335,9 +341,11 @@
 	CommandCost total_cost(EXPENSES_NEW_VEHICLES);
 	uint32 build_argument = 2;

+	p1 = GB(p1, 0, 16);
 	if (!IsValidVehicleID(p1)) return CMD_ERROR;

 	Vehicle *v = GetVehicle(p1);
+	if (!v->IsPrimaryVehicle()) return CMD_ERROR;
 	Vehicle *v_front = v;
 	Vehicle *w = NULL;
 	Vehicle *w_front = NULL;
@@ -519,7 +527,7 @@
 {
 	VehicleList list;

-	GenerateVehicleSortList(&list, type, owner, id, vlw_flag);
+	if (!GenerateVehicleSortList(&list, type, owner, id, vlw_flag)) return CMD_ERROR;

 	/* Send all the vehicles to a depot */
 	for (uint i = 0; i < list.Length(); i++) {
@@ -546,10 +554,11 @@
  */
 CommandCost CmdRenameVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
+	p1 = GB(p1, 0, 16);
 	if (!IsValidVehicleID(p1)) return CMD_ERROR;

 	Vehicle *v = GetVehicle(p1);
-	if (!CheckOwnership(v->owner)) return CMD_ERROR;
+	if (!v->IsPrimaryVehicle() || !CheckOwnership(v->owner)) return CMD_ERROR;

 	bool reset = StrEmpty(text);

@@ -577,13 +586,14 @@
  */
 CommandCost CmdChangeServiceInt(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
+	p1 = GB(p1, 0, 16);
 	uint16 serv_int = GetServiceIntervalClamped(p2); // Double check the service interval from the user-input

 	if (serv_int != p2 || !IsValidVehicleID(p1)) return CMD_ERROR;

 	Vehicle *v = GetVehicle(p1);

-	if (!CheckOwnership(v->owner)) return CMD_ERROR;
+	if (!v->IsPrimaryVehicle() || !CheckOwnership(v->owner)) return CMD_ERROR;

 	if (flags & DC_EXEC) {
 		v->service_interval = serv_int;
Index: src/train_cmd.cpp
===================================================================
--- src/train_cmd.cpp	(revision 19678)
+++ src/train_cmd.cpp	(working copy)
@@ -724,6 +724,7 @@
  */
 CommandCost CmdBuildRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
+	p1 = GB(p1, 0, 16);
 	/* Check if the engine-type is valid (for the company) */
 	if (!IsEngineBuildable(p1, VEH_TRAIN, _current_company)) return_cmd_error(STR_RAIL_VEHICLE_NOT_AVAILABLE);

Index: src/tunnelbridge_cmd.cpp
===================================================================
--- src/tunnelbridge_cmd.cpp	(revision 19678)
+++ src/tunnelbridge_cmd.cpp	(working copy)
@@ -215,7 +215,7 @@
 			break;

 		case TRANSPORT_RAIL:
-			railtype = (RailType)GB(p2, 8, 7);
+			railtype = (RailType)GB(p2, 8, 4);
 			if (!ValParamRailtype(railtype)) return CMD_ERROR;
 			break;

@@ -479,7 +479,7 @@

 	_build_tunnel_endtile = 0;
 	if (transport_type == TRANSPORT_RAIL) {
-		if (!ValParamRailtype((RailType)p1)) return CMD_ERROR;
+		if (!ValParamRailtype((RailType)GB(p1, 0, 4))) return CMD_ERROR;
 	} else {
 		const RoadTypes rts = (RoadTypes)GB(p1, 0, 2);
 		if (!AreValidRoadTypes(rts) || !HasRoadTypesAvail(_current_company, rts)) return CMD_ERROR;
Index: src/waypoint_cmd.cpp
===================================================================
--- src/waypoint_cmd.cpp	(revision 19678)
+++ src/waypoint_cmd.cpp	(working copy)
@@ -314,6 +314,7 @@
  */
 CommandCost CmdRenameWaypoint(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
+	p1 = GB(p1, 0, 16);
 	if (!IsValidWaypointID(p1)) return CMD_ERROR;

 	Waypoint *wp = GetWaypoint(p1);
Index: src/water_cmd.cpp
===================================================================
--- src/water_cmd.cpp	(revision 19678)
+++ src/water_cmd.cpp	(working copy)
@@ -288,7 +288,7 @@
 	int y;
 	int sx, sy;

-	if (p1 >= MapSize()) return CMD_ERROR;
+	if (p1 >= MapSize() || p2 > 2) return CMD_ERROR;

 	/* Outside of the editor you can only build canals, not oceans */
 	if (p2 != 0 && _game_mode != GM_EDITOR) return CMD_ERROR;
Index: src/economy.cpp
===================================================================
--- src/economy.cpp	(revision 19678)
+++ src/economy.cpp	(working copy)
@@ -1504,7 +1504,6 @@
 	/* Handle end of route payment */
 	Money profit = DeliverGoods(count, this->ct, cp->source, this->current_station, cp->source_xy, cp->days_in_transit, this->owner);
 	this->route_profit += profit;
-
 	/* The vehicle's profit is whatever route profit there is minus feeder shares. */
 	this->visual_profit += profit - cp->feeder_share;
 }
@@ -1929,6 +1928,7 @@
  */
 CommandCost CmdBuyShareInCompany(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
+	p1 = GB(p1, 0, 8);
 	CommandCost cost(EXPENSES_OTHER);

 	/* Check if buying shares is allowed (protection against modified clients)
@@ -1974,6 +1974,7 @@
  */
 CommandCost CmdSellShareInCompany(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
+	p1 = GB(p1, 0, 8);
 	/* Check if selling shares is allowed (protection against modified clients)
 	 * Cannot sell own shares */
 	if (!IsValidCompanyID((CompanyID)p1) || !_settings_game.economy.allow_shares || _current_company == (CompanyID)p1) return CMD_ERROR;
@@ -2007,6 +2008,7 @@
  */
 CommandCost CmdBuyCompany(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
+	p1 = GB(p1, 0, 8);
 	CompanyID cid = (CompanyID)p1;

 	/* Disable takeovers in multiplayer games */
Index: src/rail.cpp
===================================================================
--- src/rail.cpp	(revision 19678)
+++ src/rail.cpp	(working copy)
@@ -175,7 +175,7 @@

 bool ValParamRailtype(const RailType rail)
 {
-	return HasRailtypeAvail(_current_company, rail);
+	return rail < RAILTYPE_END && HasRailtypeAvail(_current_company, rail);
 }

 RailType GetBestRailtype(const CompanyID company)
Index: src/roadveh_cmd.cpp
===================================================================
--- src/roadveh_cmd.cpp	(revision 19678)
+++ src/roadveh_cmd.cpp	(working copy)
@@ -165,6 +165,7 @@
 	Vehicle *v;
 	UnitID unit_num;

+	p1 = GB(p1, 0, 16);
 	if (!IsEngineBuildable(p1, VEH_ROAD, _current_company)) return_cmd_error(STR_ROAD_VEHICLE_NOT_AVAILABLE);

 	const Engine *e = GetEngine(p1);
Index: src/aircraft_cmd.cpp
===================================================================
--- src/aircraft_cmd.cpp	(revision 19678)
+++ src/aircraft_cmd.cpp	(working copy)
@@ -248,6 +248,7 @@
  */
 CommandCost CmdBuildAircraft(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
+	p1 = GB(p1, 0, 16);
 	if (!IsEngineBuildable(p1, VEH_AIRCRAFT, _current_company)) return_cmd_error(STR_AIRCRAFT_NOT_AVAILABLE);

 	const AircraftVehicleInfo *avi = AircraftVehInfo(p1);

